from datetime import datetime

from sqlobject import *

from turbogears import identity 
from turbogears.database import PackageHub

hub = PackageHub("arkivo")
__connection__ = hub


# class YourDataClass(SQLObject):
#     pass

class Setting(SQLObject):
    name = StringCol(length=60, alternateID=True)
    data = PickleCol()
    
class Service(SQLObject):
    name = StringCol(length=60, alternateID=True)
    server = StringCol(length=255)
    port = IntCol(default=6667)
    nickname = StringCol(length=60)
    password = StringCol(length=60, default=None)
    username = StringCol(length=60, default=None)
    localaddress = StringCol(length=15, default="")
    localport = IntCol(default=0)
    
    channels = MultipleJoin("Channel")
    
    @classmethod
    def byName(self, name):
        service = None
        try:
            return self.select(Service.q.name==name)
        except SQLObjectNotFound:
            for s in self.select():
                if service.name.lower() == name.lower():
                     return s
            raise
    
    def _get_connection_dict(self):
        d = {}
        d["server"] = self.server
        d["port"] = self.port
        d["nickname"] = self.nickname
        d["password"] = self.password
        d["username"] = self.username
        d["localaddress"] = self.localaddress
        d["localport"] = self.localport
        return d
    
class Channel(SQLObject):
    class sqlmeta:
        orderBy = "name"
    service = ForeignKey("Service")
    name = UnicodeCol(length=60)
    topic = UnicodeCol(default=None)
    messages = MultipleJoin("Message")
    enabled = BoolCol()
    
    @classmethod
    def by_name_and_service(self, chan, service):
        service = Service.byName(service)
        #I'm going to assume that there will only ever be one
        # unique channel name for each service.
        # --"Compound keys, my kingdom for compound keys"
        return Channel.select(AND(Channel.q.serviceID==service.id, Channel.q.name==chan))[:1][0]
        
    def __str__(self):
        return self.name
        
    def last_messages(self, n=100):
        m = Message.select(Message.q.channelID == self.id)
        m_count = m.count()
        if m_count > n:
            return m[m_count-n:m_count]
        else:
            return m
            
    def day_changed(self):
        # This is purely so my templates don't break.
        return False
        
    @classmethod
    def by_name(self, service, name):
        """ Pass a service instance to this"""
        chan = Channel.select(AND(Channel.q.name == name, Channel.q.serviceID == service.id))[0]
        return chan
    
        
class Message(SQLObject):
    channel = ForeignKey("Channel")
    time = DateTimeCol()
    nick = UnicodeCol(length=30)
    line = UnicodeCol()
    isaction = BoolCol()
    
    def __str__(self):
        return "%s: %s" % (self.nick, self.line)
        
    def _get_fancyline(self):
        pass
            

class VisitIdentity(SQLObject):
    visit_key = StringCol( length=40, alternateID=True,
                          alternateMethodName="by_visit_key" )
    user_id = IntCol()


class Group(SQLObject):
    """
    An ultra-simple group definition.
    """
    
    # names like "Group", "Order" and "User" are reserved words in SQL
    # so we set the name to something safe for SQL
    class sqlmeta:
        table="tg_group"
    
    group_name = UnicodeCol( length=16, alternateID=True,
                            alternateMethodName="by_group_name" )
    display_name = UnicodeCol( length=255 )
    created = DateTimeCol( default=datetime.now )

    # collection of all users belonging to this group
    users = RelatedJoin( "User", intermediateTable="user_group",
                        joinColumn="group_id", otherColumn="user_id" )

    # collection of all permissions for this group
    permissions = RelatedJoin( "Permission", joinColumn="group_id", 
                              intermediateTable="group_permission",
                              otherColumn="permission_id" )


class User(SQLObject):
    """
    Reasonably basic User definition. Probably would want additional attributes.
    """
    # names like "Group", "Order" and "User" are reserved words in SQL
    # so we set the name to something safe for SQL
    class sqlmeta:
        table="tg_user"

    user_name = UnicodeCol( length=16, alternateID=True,
                           alternateMethodName="by_user_name" )
    email_address = UnicodeCol( length=255, alternateID=True,
                               alternateMethodName="by_email_address" )
    display_name = UnicodeCol( length=255 )
    password = UnicodeCol( length=40 )
    created = DateTimeCol( default=datetime.now )

    # groups this user belongs to
    groups = RelatedJoin( "Group", intermediateTable="user_group",
                         joinColumn="user_id", otherColumn="group_id" )

    def _get_permissions( self ):
        perms = set()
        for g in self.groups:
            perms = perms | set(g.permissions)
        return perms
        
    def _set_password( self, cleartext_password ):
        "Runs cleartext_password through the hash algorithm before saving."
        hash = identity.encrypt_password(cleartext_password)
        self._SO_set_password(hash)
        
    def set_password_raw( self, password ):
        "Saves the password as-is to the database."
        self._SO_set_password(password)



class Permission(SQLObject):
    permission_name = UnicodeCol( length=16, alternateID=True,
                                 alternateMethodName="by_permission_name" )
    description = UnicodeCol( length=255 )
    
    groups = RelatedJoin( "Group",
                        intermediateTable="group_permission",
                         joinColumn="permission_id", 
                         otherColumn="group_id" )


